#include "config.h"

#include "libbase.h"

#define CRYPT_C1            183
#define CRYPT_C2            73

static int 
rand_mask(int mask)
{
	/* Came from srand48() man page */
	return (int)(mask * 0x5deece66dLL + 0xb) % 256;
}

char *
ecm_encrypt(const char *data, ecm_size_t len)
{
	/* returns dynamic allocated buffer of len */
	unsigned char	*org, *enc;
	unsigned	i, c, s, m;

	org = (unsigned char *)ecm_malloc(len);
	enc = (unsigned char *)ecm_malloc(len);
	memcpy(org, data, len);

	/* Shift buffer */

	c = 0;
	for (i = 0; i < len; i++)
		c += org[i];

	s = (c + 7) * CRYPT_C1;
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	memcpy(org, enc, len);
	s = ((unsigned int)(c + 3) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	memcpy(org, enc, len);
	s = ((unsigned int)(c * c + 13) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	memcpy(org, enc, len);
	s = (int)((c * 0x5deece66dLL + 0xb) * CRYPT_C1);
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	/* Encrypt! */
	enc[0] ^= CRYPT_C2;
	m = rand_mask(enc[0] + CRYPT_C2);
	for (i = 1; i < len; i++) {
		enc[i] = enc[i] ^ m;
		m = rand_mask(m);
	}

	memcpy(org, enc, len);
	c = 0;
	for (i = 0; i < len; i++)
		c += org[i];

	s = ((unsigned int)(c * c + 3) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	memcpy(org, enc, len);
	s = (int)((c * 0x5deece66dLL + 0xb + 17) * CRYPT_C1);
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		enc[i] = org[(i + s) % len]; 
	for (; i < len; i++)
		enc[len - 1 + s - i] = org[(i + s) % len];

	ecm_free(org);
	return (char *)enc;
}

char *
ecm_decrypt(const char *encdata, ecm_size_t len)
{
	/* returns dynamic allocated buffer of len */
	unsigned char	*enc, *org;
	unsigned	i, c, s, m;

	enc = (unsigned char *)ecm_malloc(len);
	org = (unsigned char *)ecm_malloc(len);
	memcpy(enc, encdata, len);

	c = 0;
	for (i = 0; i < len; i++)
		c += enc[i];

	s = (int)(c * 0x5deece66dLL + 0xb + 17) * CRYPT_C1;
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];
	memcpy(enc, org, len);

	s = ((unsigned int)(c * c + 3) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];
	memcpy(enc, org, len);

	/* Decrypt! */
	m = rand_mask(enc[0] + CRYPT_C2);
	enc[0] ^= CRYPT_C2;
	for (i = 1; i < len; i++) {
		enc[i] = enc[i] ^ m;
		m = rand_mask(m);
	}

	/* Unshift buffer */

	c = 0;
	for (i = 0; i < len; i++)
		c += enc[i];

	s = (int)(c * 0x5deece66dLL + 0xb) * CRYPT_C1;
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];
	memcpy(enc, org, len);

	s = ((unsigned int)(c * c + 13) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];
	memcpy(enc, org, len);

	s = ((unsigned int)(c + 3) * CRYPT_C1) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];
	memcpy(enc, org, len);

	s = (c + 7) * CRYPT_C1;
	s = ((unsigned int)(s * s + s)) % len;
	for (i = 0; i < s; i++)
		org[(i + s) % len] = enc[i]; 
	for (; i < len; i++)
		org[(i + s) % len] = enc[len - 1 + s - i];

	ecm_free(enc);
	return (char *)org;
}
